// ProjectName: go-learn-demo
// PackageName: _5_http_server
// Description:
// Author: hy.bai
// Date: 2025-08-01 21:52

package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log/slog"
	"net/http"
	"time"
)

// 处理Post请求
// http.ResponseWriter对象的作用是控制响应信息
// http.Request获取请求信息
// 实现步骤: 1.获取请求信息; 2.处理请求; 3.返回响应
func handlerPost(resp http.ResponseWriter, req *http.Request) {
	// 1.接收请求信息
	data, err := io.ReadAll(req.Body)
	if err != nil {
		msg := fmt.Sprintf("接收参数发生错误:%s", err.Error())
		slog.Error(msg)
		responseJson(resp, 4001, msg, nil)
		return
	}
	// 定义一个匿名结构体用于接收请求参数
	var param = new(struct {
		Name string `json:"name"`
	})
	// 把json字符串进行反序列化
	if err := json.Unmarshal(data, &param); err != nil {
		message := fmt.Sprintf("反序列化参数发生错误:%s", err.Error())
		slog.Error(message)
		responseJson(resp, 4002, message, nil)
		return
	}
	// 判断参数是否为空
	if param != nil && param.Name == "" {
		message := fmt.Sprintf("参数[%s]不能为空", "name")
		slog.Error(message)
		responseJson(resp, 4003, message, nil)
		return
	}
	// 2.处理请求
	slog.Info(fmt.Sprintf("业务逻辑处理,name是%s", param.Name))
	// 3.返回响应
	// 响应方式1：直接响应数据
	// resp.Write([]byte(result)) // string类型的转换成byte切片
	// 响应方式2：使用json
	responseJson(resp, 2000, "success", param.Name)
}

// 处理Get请求
// http.ResponseWriter对象的作用是控制响应信息
// http.Request获取请求信息
// 注意：只能获取"http://127.0.0.1:8080/api/get?name=abc"中的参数，不能获取表单数据
func handlerGet(resp http.ResponseWriter, req *http.Request) {
	// 1.接收请求参数
	params := req.URL.Query() // 是一个Map类型,key是字符串，而value是字符串数组。
	// fmt.Printf("params的类型是%T,值是%v\n", params, params)
	// 打印结果：params的类型是url.Values,值是map[name:[abc]]
	if params != nil && len(params) > 0 {
		// 获取URL参数方式1：通过Get方法获取
		// name := params.Get("name")
		// 获取URL参数方式2:
		name := params["name"][0]
		// 获取请求头参数: 获取请求头中的参数
		// name := req.Header.Get("name")
		data := fmt.Sprintf("你好, %s~ 欢迎来到Golang的世界", name)
		// 2.处理请求
		slog.Info(fmt.Sprintf("业务处理的结果是:%s", data))
		// 3.返回响应
		responseJson(resp, 2000, "success", data)
	} else {
		message := fmt.Sprintf("请求参数[%s]不能为空", "name")
		slog.Error(message)
		// 3.返回响应
		responseJson(resp, 4000, message, nil)
	}
}

// 统一封装json响应
func responseJson(resp http.ResponseWriter, code int, msg string, result interface{}) {
	resp.WriteHeader(http.StatusOK)
	// 响应方式2：使用json
	var resultData = map[string]any{
		"code":      code,
		"msg":       msg,
		"data":      result,
		"timestamp": time.Now().UnixMilli(),
	}
	if err := json.NewEncoder(resp).Encode(resultData); err != nil {
		slog.Error("发生错误:", err.Error())
	}
}

// 启动一个Web服务
// 在golang中，你要构建一个web服务，必然要用到http.ListenAndServe,第二个参数必须要有一个handler
func main() {
	// 1.定义接口以及对应的接口处理方法
	// 使用http的HandleFunc方法定义接口地址以及Handler，handler就是接口对应处理的方法
	http.HandleFunc("/", func(resp http.ResponseWriter, request *http.Request) {
		resp.WriteHeader(http.StatusOK)
		// 定义响应结果
		var result = map[string]any{ // 注意：any等价于interface{}
			"code": 2000,
			"msg":  "success",
			"data": "hello world",
		}
		if err := json.NewEncoder(resp).Encode(result); err != nil {
			slog.Error("发生错误:", err.Error())
			return
		}
	})

	http.HandleFunc("/api/sayhello", handlerGet) // 定义一个Get请求。
	http.HandleFunc("/api/post", handlerPost)    // 定义一个Post请求。
	// 注:在net-http包里定义Get请求或Post请求并没有明确的分区请求方式，而是根据获取不同类型的参数
	// 在http协议中，可以携带参数的地方有3个：1.URL地址后面;2.请求体中;3.请求头中。

	// 2.启动Server服务
	var addr = ":8080"
	slog.Info("服务启动成功,访问地址: http://127.0.0.1" + addr)
	if err := http.ListenAndServe(addr, nil); err != nil {
		slog.Error("启动服务失败,原因是:", err.Error())
	}
	// 注意:http.ListenAndServe会阻塞main协程，所以后面的代码不会执行。
	fmt.Println("服务启动成功") // http.ListenAndServe方法后面的代码不会执行
}
